﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using CIAWorldMap;
using MapSurface;
using OSMLib;
using WGS84;

namespace LaoMap
{
    class CookieCutter
    {
        Surface _surface;

        public CookieCutter(Surface surface)
        {
            _surface = surface;
        }

        public  List<Point>  ReorderLaosBorderWays()
        {
            List<Point> points = new List<Point>();

            List<WayVoter> listOfWaysOnlaosBorder = new List<WayVoter>();

            foreach (OsmWay w in Osm.Ways.Values)
            {
                //if (_surface.WayVisible(w) && w.IsBoundary && w.IsNationalBoundary && w.IsLaosBoundary)
                 if (w.IsBoundary && w.IsNationalBoundary && w.IsLaosBoundary)
                {
                    listOfWaysOnlaosBorder.Add(new WayVoter(w));
                }
            }

            LaosBorder laosBorder = new LaosBorder();
            Dictionary<string, MapSegment> normalSegments = new Dictionary<string, MapSegment>();
            ArchiveLoader.ReadBoundaries(normalSegments, laosBorder);

            for (int x = 0; x < laosBorder._geoPolygon.Count; x += 10)
            {
                foreach (WayVoter v in listOfWaysOnlaosBorder)
                {
                    foreach (OsmNode n in v._osmWay.Nodes)
                    {
                        double d = n._wgs.Distance(new Wgs84Coordinate(laosBorder._geoPolygon[x]._lat, laosBorder._geoPolygon[x]._lon));
                        if (d < v._distance)
                        {
                            v._index = x;
                            v._distance = d;
                        }
                    }
                }
            }

            listOfWaysOnlaosBorder.Sort(new SortByIndex());


            for (int wayIndex = 0; wayIndex < listOfWaysOnlaosBorder.Count; wayIndex++)
            {
                int previousIndex = wayIndex - 1;
                if (previousIndex == -1)
                {
                    previousIndex = listOfWaysOnlaosBorder.Count - 1;
                }
                WayVoter previous = listOfWaysOnlaosBorder[previousIndex];
                WayVoter thisWay = listOfWaysOnlaosBorder[wayIndex];
                thisWay._osmWay.SetBoarderIndexTag(wayIndex);
                OsmNode firstThis = (OsmNode)thisWay._osmWay.Nodes[0];
                OsmNode lastThis = (OsmNode)thisWay._osmWay.Nodes[thisWay._osmWay.Nodes.Count - 1];

                OsmNode previousFirst = (OsmNode)previous._osmWay.Nodes[0];
                OsmNode previouslast = (OsmNode)previous._osmWay.Nodes[previous._osmWay.Nodes.Count - 1];

                OsmNode previousNearest = previous._reverseRender ? previouslast : previousFirst;

                double distanceFromFirst = previousNearest._wgs.Distance(firstThis._wgs);
                double distanceFromLast = previousNearest._wgs.Distance(lastThis._wgs);

                if (distanceFromLast < distanceFromFirst)
                {
                    thisWay._reverseRender = true;
                    thisWay._osmWay.Nodes.Reverse(); // this changes the ordering of the nodes in the border ways
                }

                // seems our wonderful logic is a bit naff. Noticed on the Laos border map backdrop as a spike.

                if (thisWay._index == 1780)
                {
                    thisWay._osmWay.Nodes.Reverse();
                }

                foreach (OsmNode n1 in thisWay._osmWay.Nodes)
                {
                   // Debug.WriteLine("" + n1._wgs.Longitude + " " + n1._wgs.Latitude + " " + thisWay._index);
                    points.Add(new Point(n1._wgs.Longitude, n1._wgs.Latitude));
                }
            }
            return points;
        }
 

        public void ShowBorder(List<Coord> geoPolygon, Canvas canvas1)
        {
            LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
            myLinearGradientBrush.StartPoint = new Point(0, 0);
            myLinearGradientBrush.EndPoint = new Point(1, 1);
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.Yellow, 0.0));
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.Red, 0.25));
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.Blue, 0.75));
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.LimeGreen, 1.0));

            canvas1.Children.Clear();
            //SolidColorBrush br = new SolidColorBrush(Colors.GreenYellow);

            var polly = new Polyline
            {
                StrokeThickness = 0,
                Fill = myLinearGradientBrush,
                Points = new PointCollection(),
                StrokeStartLineCap = PenLineCap.Round,
                StrokeEndLineCap = PenLineCap.Round,
                StrokeLineJoin = PenLineJoin.Round
            };

            // polly.MouseDown += new MouseButtonEventHandler(ElMouseDown);

            foreach (Coord n1 in geoPolygon)
            {
                double px = _surface.GetPx(n1._lon);
                double py = _surface.GetPy(n1._lat);
                polly.Points.Add(new Point(px, py));
            }

            if (polly.Points.Count != 0)
                canvas1.Children.Add(polly);
        }

        private void Straddle(LaosBorder laosBorder, OsmWay w)
        {
            bool totallyInside = true;
            foreach (OsmNode node in w.Nodes)
            {
                if (!laosBorder.PointInPolygon(new Coord(node._wgs.Latitude, node._wgs.Longitude)))
                {
                    totallyInside = false;
                    break;
                }
            }
            // straddling
            if (totallyInside)
            {
                Osm.Ways.Add(w._id, w);
                return;
            }

            List<IOsmNode> nodes = new List<IOsmNode>();

            bool inserting = false;
            foreach (OsmNode n in w.Nodes)
            {
                if (inserting || laosBorder.PointInPolygon(new Coord(n._wgs.Latitude, n._wgs.Longitude)))
                {
                    nodes.Add(n);
                    inserting = true;
                }
            }

            nodes.Reverse();
            List<IOsmNode> nodesEnd = new List<IOsmNode>();

            inserting = false;
            foreach (OsmNode n in nodes)
            {
                if (inserting || laosBorder.PointInPolygon(new Coord(n._wgs.Latitude, n._wgs.Longitude)))
                {
                    nodesEnd.Add(n);
                    inserting = true;
                }
            }

            nodesEnd.Reverse();
            w.Nodes.Clear();
            w.Nodes.AddRange(nodesEnd);
            if (w.Nodes.Count > 1)
            {
                Osm.Ways.Add(w._id, w);
            }
        }

        public void Cut()
        {
            List<Coord> geoPolygon = new List<Coord>();

            List<OsmWay> listOfWaysOnlaosBorder = new List<OsmWay>();

            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (w.IsBoundary && w.IsNationalBoundary && w.IsLaosBoundary)
                {
                    listOfWaysOnlaosBorder.Add(w);
                }
            }

            listOfWaysOnlaosBorder.Sort(new SortByTagIndex());

            foreach (var way in listOfWaysOnlaosBorder)
            {
                foreach (OsmNode node in way.Nodes)
                {
                    geoPolygon.Add(new Coord(node._wgs.Latitude, node._wgs.Longitude));
                }
            }

           // ShowBorder(geoPolygon);
           // _osmRenderer.DoOsmPlotBorder(canvas1);

            LaosBorder laosBorder = new LaosBorder();
            laosBorder._geoPolygon = geoPolygon;

            // nodes
            {
                IOsmNode[] list = new IOsmNode[Osm.NamedNodes.Count];
                Osm.NamedNodes.CopyTo(list);

                Osm.NamedNodes.Clear();
                foreach (OsmNode n in list)
                {
                    if (laosBorder.PointInPolygon(new Coord(n._wgs.Latitude, n._wgs.Longitude)))
                    {
                        Osm.NamedNodes.Add(n);
                    }
                }
            }

            IOsmWay[] listWays = new IOsmWay[Osm.Ways.Count];
            Osm.Ways.Values.CopyTo(listWays, 0);

            Osm.Ways.Clear();
            foreach (OsmWay w in listWays)
            {
                bool tl = laosBorder.PointInPolygon(new Coord(w._northLimit, w._westLimit));
                bool tr = laosBorder.PointInPolygon(new Coord(w._northLimit, w._eastLimit));
                bool br = laosBorder.PointInPolygon(new Coord(w._southLimit, w._eastLimit));
                bool bl = laosBorder.PointInPolygon(new Coord(w._southLimit, w._westLimit));

                // totally closed
                if ((tl && tr && br && bl) || w.IsNationalBoundary)
                {
                    Osm.Ways.Add(w._id, w);
                }
                else
                {
                    if (!tl && !tr && !br && !bl)
                    {
                        // totally ouside
                    }
                    else
                    {
                        if (w.IsNaturalWater || w.IsNaturalLand)
                        {
                            Osm.Ways.Add(w._id, w);
                        }
                        else
                        {
                            Straddle(laosBorder, w);
                        }
                    }
                }
            }
        }

    }
}
